<?php

namespace Core\init;

use Core\annotations\Bean;
use Core\BeanFactory;
use DI\Annotation\Inject;
use Illuminate\Database\Capsule\Manager as lvdb;

/**
 * @method \Illuminate\Database\Query\Builder  table(string $table, string|null $connection = null)
 * @Bean()
 */
class MyDB
{

    /**
     * @var lvdb lvdb
     */
    private $lvDB;

    /**
     * @var
     */
    private $dbSource = 'default';

    /**
     * 事务DB对象
     * @var bool
     */
    private $transactionDB = false;

    /**
     * 连接池对象
     * @var PDOPool
     */
    private $pdopool;

    /**
     * MyDB constructor.
     * @param bool $db_obj
     * @throws \Exception
     */
    public function __construct($db_obj = false)
    {
        global $GLOBAL_CONFIGS;

        //支持单数据源
        /*if (isset($GLOBAL_CONFIGS['db']) && isset($GLOBAL_CONFIGS['db']['default'])) {
            $this->lvDB = new lvdb();
            $this->lvDB->addConnection($GLOBAL_CONFIGS['db']['default']);
            $this->lvDB->setAsGlobal();
            $this->lvDB->bootEloquent();
        }*/


        //支持多数据源
        if (isset($GLOBAL_CONFIGS['db'])) {
            $configs = $GLOBAL_CONFIGS['db'];
            $this->lvDB = new lvdb();

            foreach ($configs as $k => $v) {
//                $this->lvDB->addConnection($v, $k);
                $this->lvDB->addConnection(['driver' => 'mysql'], $k);
            }

            $this->lvDB->setAsGlobal();
            $this->lvDB->bootEloquent();
        }

        //事务
        $this->transactionDB = $db_obj;
        //初始化连接池对象
        $this->pdopool = BeanFactory::getBean(PDOPool::class);
        if ($db_obj) { //如果有值，则代表处在事务进程中，则直接设置pdo对象和开启事务
            $this->lvDB->getConnection($this->dbSource)->setPdo($this->transactionDB->db);
            $this->lvDB->getConnection($this->dbSource)->beginTransaction();
        }
    }

    /**
     * 开启事务
     */
    public function begin()
    {
        return new self($this->pdopool->getConnection());
    }

    /**
     * 提交事务
     */
    public function commit()
    {
        try {
            $this->lvDB->getConnection($this->dbSource)->commit();
        } catch (\Throwable $e) {
            echo __METHOD__ . '::' . $e->getMessage() . '-File :' . $e->getFile() . '-Line:' . $e->getLine();

        } finally {
            if ($this->transactionDB) {
                //回归连接池
                $this->pdopool->close($this->transactionDB);
                //重置事务为false
                $this->transactionDB = false;
            }
        }
    }

    /**
     * 回滚事务
     */
    public function rollback()
    {
        try {
            $this->lvDB->getConnection($this->dbSource)->rollBack();
        } catch (\Throwable $e) {
            echo __METHOD__ . '::' . $e->getMessage() . '-File :' . $e->getFile() . '-Line:' . $e->getLine();
        } finally {
            if ($this->transactionDB) {
                //回归连接池
                $this->pdopool->close($this->transactionDB);
                //重置事务为false
                $this->transactionDB = false;
            }
        }
    }

    /**
     * 释放链接 （专供DBModel使用）
     * @param $pdo_object
     */
    public function releaseConnection($pdo_object)
    {
        if ($pdo_object && !$this->transactionDB) {
            $this->pdopool->close($pdo_object); //回归连接池
        }
    }

    /**
     * 创建获取PDO链接对象  （专供DBModel使用）
     * @return bool|mixed
     */
    public function generateConnection()
    {
        $isTransaction = false;

        if ($this->transactionDB) { //true 表示正在事务进程中
            $pdo_object = $this->transactionDB;
            $isTransaction = true;
        } else {
            $pdo_object = $this->pdopool->getConnection();
        }

        //只有不在事务中才需要设置pdo对象
        if (!$isTransaction && $pdo_object) {
            $this->lvDB->getConnection($this->dbSource)->setPdo($pdo_object->db);
            return $pdo_object;
        }
        return false;
    }

    /**
     * 魔术方法
     * @param $methodName
     * @param $arguments
     * @return mixed
     */
    public function __call($methodName, $arguments)
    {
        //非连接池模式
        //return $this->lvDB::$methodName(...$arguments);
        //return $this->lvDB::connection($this->dbSource)->$methodName(...$arguments);

        //是否开启事务
        $isTransaction = false;
        if ($this->transactionDB) { //true表示正在事务进程中
            $pdo_object = $this->transactionDB;
            $isTransaction = true;
        } else {
            $pdo_object = $this->pdopool->getConnection();
        }

        //连接池模式
        try {

            if (!$pdo_object) return [];

            //TODO:保持简单，暂不考虑数据源切换！
            $this->lvDB->getConnection($this->dbSource)->setPdo($pdo_object->db);

            //如果在事务进程中，则开启事务  TODO：去掉，会造成多次开启事务
//            if ($isTransaction) {
//                $this->lvDB->getConnection($this->dbSource)->beginTransaction();
//            }

            $result = $this->lvDB::connection($this->dbSource)->$methodName(... $arguments);

            return $result;
        } catch (\Throwable $e) {

            echo __METHOD__ . '::' . $e->getMessage() . '-File :' . $e->getFile() . '-Line:' . $e->getLine();

            return null;
        } finally {
            //非事务中才执行close回归链接
            if ($pdo_object && !$isTransaction) {
                $this->pdopool->close($pdo_object);
            }
        }


    }

    /**
     * @return mixed
     */
    public function getDbSource()
    {
        return $this->dbSource;
    }

    /**
     * @param mixed $dbSource
     */
    public function setDbSource($dbSource)
    {
        $this->dbSource = $dbSource;
        return $this;
    }
}